import { EventEmitter } from '../../common/event-emitter';
import { TimerManager } from '../../common/timer-manager';
import { debounce } from '../../flow/timing';
import { DownloadUIController, UIControllerEvent } from './ui-controller';

export enum StateEvent {
  DownloadStart = 't1h0.ui.downloader.state-manager.DownloadStart',
  DownloadToggle = 't1h0.ui.downloader.state-manager.DownloadToggle',
  DownloadPause = 't1h0.ui.downloader.state-manager.DownloadPause',
  DownloadResume = 't1h0.ui.downloader.state-manager.DownloadResume',
  DownloadCancel = 't1h0.ui.downloader.state-manager.DownloadCancel',
  DownloadComplete = 't1h0.ui.downloader.state-manager.DownloadComplete',
  ProgressReset = 't1h0.ui.downloader.state-manager.ProgressReset',
  ProgressUpdate = 't1h0.ui.downloader.state-manager.ProgressUpdate',
  ProgressPause = 't1h0.ui.downloader.state-manager.ProgressPause',
  ProgressResume = 't1h0.ui.downloader.state-manager.ProgressResume',
  StatusChange = 't1h0.ui.downloader.state-manager.StatusChange'
}

/**
 * 下载状态枚举
 * 表示Downloader实例的不同生命周期状态
 */
export enum DownloadStatus {
  /** 初始状态，未开始下载 */
  Idle = 't1h0.ui.downloader.status.idle',
  /** 正在下载中 */
  Downloading = 't1h0.ui.downloader.status.downloading',
  /** 已暂停 */
  Paused = 't1h0.ui.downloader.status.paused',
  /** 下载完成 */
  Completed = 't1h0.ui.downloader.status.completed',
  /** 已取消 */
  Cancelled = 't1h0.ui.downloader.status.cancelled',
  /** 下载错误 */
  Error = 't1h0.ui.downloader.status.error'
}

export type Progress = {
  downloadedSize: number;
  totalSize: number;
  speed: number;
  lastUpdated: number;
  lastDownloadedSize: number;
}

export type Meta = {
  totalSize: number;
  chunkSize: number;
  chunkCount: number;
}

/**
 * 下载状态管理类
 * 负责管理下载过程中的所有状态数据、状态转换逻辑和下载元数据
 * 实现了进度跟踪、暂停/恢复控制、下载速度计算和资源清理等核心功能
 * 通过事件系统通知状态变更，实现与UI控制器和下载器的解耦通信
 */
export class DownloadStateManager extends EventEmitter {
  /**
   * 当前下载状态
   * @type {DownloadStatus}
   * @default DownloadStatus.Idle
   * @private
   */
  private __status: DownloadStatus = DownloadStatus.Idle;

  /**
   * 下载进度数据对象
   * 包含下载过程中的关键进度指标和计算依据
   * @private
   * @property {number} downloadedSize - 已下载字节数
   * @property {number} totalSize - 文件总字节数
   * @property {number} speed - 当前下载速度(字节/秒)
   * @property {number} lastUpdated - 上次进度更新时间戳(毫秒)
   * @property {number} lastDownloadedSize - 上次记录的已下载字节数
   */
  private __progress: Progress = {
    /** 已下载大小 */
    downloadedSize: 0,
    /** 总大小 */
    totalSize: 0,
    /** 下载速度 */
    speed: 0,
    /** 上次更新时间 */
    lastUpdated: 0,
    /** 上次下载大小 */
    lastDownloadedSize: 0
  };

  private __meta: Meta = {
    totalSize: 0,
    chunkSize: 0,
    chunkCount: 0,
  }

  /**
   * 用于取消请求的控制器
   * 管理当前活跃下载请求的中止信号
   * @type {AbortController | null}
   * @private
   */
  private __abortController: AbortController | null = null;

  /**
   * 点击锁状态
   * 防止短时间内重复点击导致的操作冲突
   * @type {boolean}
   * @default false
   * @private
   */
  private __clickLock = false;

  /**
   * 速度重置定时器ID
   * 用于跟踪当前活跃的速度重置计时器
   * @type {number | null}
   * @private
   */
  private __speedResetTimerId: number | null = null;

  /**
   * 计时器管理器实例
   * 用于安全管理所有下载相关的计时器
   * @type {TimerManager}
   * @private
   */
  private __timerManager: TimerManager = new TimerManager();

  private __uiController: DownloadUIController;

  /**
   * 创建下载状态管理器实例
   * 初始化所有状态属性为默认值并设置事件绑定
   */
  constructor(UIController: DownloadUIController) {
    super();
    this.__uiController = UIController;
    this.__bindEvents();
  }

  private __bindEvents(): void {
    /** 处理用户点击下载按钮的逻辑，根据当前状态切换下载状态 */
    this.__uiController.on(StateEvent.DownloadToggle, () => {
      if (this.isClickLocked()) return;
      this.__clickLock = true;
      if (this.__status === DownloadStatus.Idle) {
        this.emit(StateEvent.DownloadStart);
      } else {
        if (this.__status === DownloadStatus.Downloading) {
          this.__status = DownloadStatus.Paused;
          this.emit(StateEvent.DownloadPause);
        } else if (this.__status === DownloadStatus.Paused) {
          this.__status = DownloadStatus.Downloading;
          this.emit(StateEvent.DownloadResume);
        }
      }

      debounce(() => {
        this.__clickLock = false
      }, 300)();
    });
    /** 处理用户点击取消按钮的逻辑，将下载状态设置为已取消并重置进度 */
    this.__uiController.on(StateEvent.DownloadCancel, () => {
      this.__status = DownloadStatus.Cancelled;
      this.__abortController?.abort();
      this.emit(StateEvent.DownloadCancel);
      // 触发进度重置事件
      this.emit(StateEvent.ProgressReset);
    })

    this.on(UIControllerEvent.ProgressUpdate, (progress: Progress) => {
      this.__uiController.emit(UIControllerEvent.ProgressUpdate, progress);
    })

    this.on(StateEvent.ProgressReset, () => {
      this.__status = DownloadStatus.Idle;
      this.__abortController = null;
      this.__progress = {
        downloadedSize: 0,
        totalSize: 0,
        speed: 0,
        lastUpdated: 0,
        lastDownloadedSize: 0
      };
      this.clearTimers();
      this.__uiController.emit(UIControllerEvent.ProgressReset);
    });
    this.on(StateEvent.ProgressUpdate, (progress: Progress) => {
      this.__progress = Object.assign(this.__progress, progress);
    })

    this.on(StateEvent.DownloadComplete, () => {
      this.__status = DownloadStatus.Completed;
      this.clearMeta();
      this.clearTimers();
      this.__uiController.emit(UIControllerEvent.ProgressComplete);
    })
  }

  updateMeta<K extends keyof Meta>(name: K, value: Meta[K]) {
    this.__meta[name] = value;
  }

  getMeta<K extends keyof Meta>(name: K): Meta[K] {
    return this.__meta[name];
  }

  clearMeta() {
    this.__meta = {
      totalSize: 0,
      chunkSize: 0,
      chunkCount: 0,
    }
  }

  /**
   * 更新下载进度数据并计算百分比和速度
   * @param {number} downloaded - 当前已下载字节数
   * @remarks 百分比会被限制在0-100范围内，速度通过calculateSpeed方法计算
   */
  updateProgress(downloaded: number): void {
    const total = this.__meta.totalSize;
    if (total <= 0) return;

    // 更新进度数据
    this.__progress.downloadedSize = downloaded;
    this.__progress.totalSize = total;
    this.__progress.speed = this.__calculateSpeed(downloaded);
    this.__progress.lastUpdated = Date.now();

    this.emit(UIControllerEvent.ProgressUpdate, this.__progress);
  }

  /**
   * 清除所有活跃计时器并清空计时器集合
   * 防止下载完成或取消后仍有计时器触发
   * @returns {void}
   */
  clearTimers(): void {
    this.__timerManager.clearAll();
  }

  /**
   * 创建新的中止控制器并返回其信号
   * 如果已有控制器则会被覆盖
   */
  createAbortController(): void {
    this.__abortController = new AbortController();
  }

  getAbortController(): AbortController | null {
    return this.__abortController;
  }

  /**
   * 中止当前下载请求
   * 调用AbortController的abort方法并清除控制器引用
   * @returns {void}
   * @remarks 如果没有活跃的中止控制器，此方法不执行任何操作
   */
  abortDownload(): void {
    if (this.__abortController) {
      this.__abortController.abort();
      this.__abortController = null;
    }
  }

  /**
   * 从活跃计时器集合中移除指定计时器ID
   * @param {number} timerId - 要移除的计时器ID
   * @returns {void}
   */
  removeTimer(timerId: number): void {
    this.__timerManager.removeTimer(timerId);
  }

  /**
   * 设置点击锁状态
   * @param {boolean} locked - true表示锁定，false表示解锁
   * @returns {void}
   */
  setClickLock(locked: boolean): void {
    this.__clickLock = locked;
  }

  /**
   * 检查当前下载状态是否为空闲状态
   * @returns {boolean} 如果状态是DownloadStatus.Idle则返回true，否则返回false
   */
  isIdleStatus(): boolean {
    return this.__status === DownloadStatus.Idle;
  }

  /**
   * 检查当前下载状态是否为取消状态
   * @returns {boolean} 如果状态是DownloadStatus.Cancelled则返回true，否则返回false
   */
  isCancelledStatus(): boolean {
    return this.__status === DownloadStatus.Cancelled;
  }

  /**
   * 检查当前下载状态是否为暂停状态
   * @returns {boolean} 如果状态是DownloadStatus.Paused则返回true，否则返回false
   */
  isPausedStatus(): boolean {
    return this.__status === DownloadStatus.Paused;
  }

  /**
   * 检查当前下载状态是否为下载中状态
   * @returns {boolean} 如果状态是DownloadStatus.Downloading则返回true，否则返回false
   */
  isDownloadingStatus(): boolean {
    return this.__status === DownloadStatus.Downloading;
  }

  /**
   * 获取当前下载状态
   * @returns {DownloadStatus} 当前下载状态枚举值
   */
  getStatus(): DownloadStatus { return this.__status; }

  /**
   * 获取当前下载进度数据的副本
   * @returns {Object} 包含下载进度信息的对象
   * @property {number} downloadedSize - 已下载字节数
   * @property {number} totalSize - 文件总字节数
   * @property {number} speed - 下载速度(字节/秒)
   * @property {number} lastUpdated - 上次更新时间戳
   * @property {number} lastDownloadedSize - 上次记录的已下载字节数
   */
  getProgress(): typeof this.__progress { return { ...this.__progress }; }

  /**
   * 检查当前是否处于点击锁定状态
   * @returns {boolean} 如果点击被锁定则返回true，否则返回false
   */
  isClickLocked(): boolean { return this.__clickLock; }

  /**
   * 设置下载状态并触发状态变更事件
   * @param {DownloadStatus} status - 新的下载状态
   * @returns {void}
   * @emits status-change 当状态发生变化时触发，包含新旧状态信息
   */
  setStatus(status: DownloadStatus): void {
    const oldStatus = this.__status;
    this.__status = status;
    this.emit(StateEvent.StatusChange, { oldStatus, newStatus: status });
  }

  /**
   * 计算下载速度
   * 根据两次进度更新的字节差和时间差计算平均下载速度
   * @param {number} downloaded - 当前已下载字节数
   * @returns {number} 下载速度(字节/秒)，四舍五入取整
   * @private
   * @remarks 实现了平滑处理以避免速度剧烈波动
   */
  private __calculateSpeed(downloaded: number): number {
    // 清除已存在的速度重置定时器
    if (this.__speedResetTimerId !== null) {
      this.__timerManager.removeTimer(this.__speedResetTimerId);
      this.__speedResetTimerId = null;
    }

    // 增加速度重置时间到3秒，避免网络波动导致速度频繁归零
    const SPEED_RESET_DELAY = 3000;
    // 使用timerManager创建定时器，而不是直接使用window.setTimeout
    this.__speedResetTimerId = this.__timerManager.addTimer(SPEED_RESET_DELAY, () => {
      this.__progress.speed = 0;
      this.__speedResetTimerId = null;
    });

    const now = Date.now();
    const timeDiff = now - this.__progress.lastUpdated;
    const MIN_TIME_DIFF = 100; // 最小时间差100ms，避免高频更新导致速度异常

    // 首次下载或时间差太小的情况
    if (this.__progress.lastUpdated === 0 || timeDiff < MIN_TIME_DIFF) {
      this.__progress.lastUpdated = now;
      this.__progress.lastDownloadedSize = downloaded;
      return this.__progress.speed; // 返回上次计算的速度，避免频繁波动
    }

    // 正常计算速度
    const diff = downloaded - this.__progress.lastDownloadedSize;
    this.__progress.lastDownloadedSize = downloaded;
    this.__progress.lastUpdated = now;

    // 计算实际速度
    const speed = Math.round((diff / (timeDiff / 1000)));

    // 平滑处理：如果速度变化超过50%，则逐渐过渡
    if (this.__progress.speed > 0 && Math.abs(speed - this.__progress.speed) / this.__progress.speed > 0.5) {
      return Math.round((this.__progress.speed + speed) / 2);
    }

    return speed;
  }
}